package com.poizon.infrastructure.lock.core.annoations.aop;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.poizon.infrastructure.common.util.CustomSpelParseUtil;
import com.poizon.infrastructure.lock.core.annoations.Lock;
import com.poizon.infrastructure.lock.core.util.ILock;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

@Slf4j
@Aspect
@AllArgsConstructor
public class LockAspect {
    private ILock iLock;

    /**
     * 分布式锁注解拦截
     */
    @SneakyThrows
    @Around("@annotation(lock)")
    public Object handleLock(ProceedingJoinPoint joinPoint, Lock lock) {
        Assert.notNull(joinPoint);
        Assert.notNull(lock);

        String lockKeySpelExpression = lock.value();
        long lockedWaitTime = lock.lockWaitTime();
        long lockTimeout = lock.lockTimeout();
        TimeUnit timeUnit = lock.timeUnit();

        String s = lock.lockTimeoutSpel();
        if (StrUtil.isNotBlank(s)) {
            lockTimeout = Long.valueOf(CustomSpelParseUtil.parseSpelValue(joinPoint, s));
        }
        String s1 = lock.lockWaitTimeSpel();
        if (StrUtil.isNotBlank(s1)) {
            lockedWaitTime = Long.valueOf(CustomSpelParseUtil.parseSpelValue(joinPoint, s1));
        }
        String s2 = lock.timeUnitSpel();
        if (StrUtil.isNotBlank(s2)) {
            timeUnit = TimeUnit.valueOf(CustomSpelParseUtil.parseSpelValue(joinPoint, s2));
        }

        log.debug(">lockKeySpelExpression={},lockedWaitTime={},lockTimeout={},timeUnit={}", lockKeySpelExpression, lockedWaitTime, lockTimeout, timeUnit);

        Assert.notBlank(lockKeySpelExpression);
        Assert.notNull(timeUnit);

        String lockKey = CustomSpelParseUtil.parseSpelValue(joinPoint, lockKeySpelExpression);
        Assert.notBlank(lockKey);

        log.debug(">lockKey={}", lockKey);

        AtomicReference<Object> resp = new AtomicReference<>();

        iLock.tryLock(
                lockKey,
                lockedWaitTime,
                lockTimeout,
                timeUnit,
                () -> {
                    try {
                        Object proceed = joinPoint.proceed();
                        resp.set(proceed);
                    } catch (Throwable throwable) {
                        throw new RuntimeException(throwable);
                    }
                },
                () -> {
                    throw new RuntimeException("获取锁失败");
                }
        );
        return resp.get();
    }


}
